﻿

using Microsoft.Office.Interop.Excel;

namespace CollisionDetection.Models
{
    using System;
    using System.Globalization;
    using System.Reflection; //For Missing.Value 
    using System.Collections.Generic;
    using System.Threading;
    using Microsoft.Office.Interop.Excel;

    public class ExcelAutomation
    {
        #region Fields

            private DataService _dataService;

        #endregion

        #region Constructor

            public ExcelAutomation()
            {
                _dataService = new DataService();
            }

        #endregion

        #region Members

        public void CreateExcelFile(string filename, IDictionary<string, Collision> collisions, List<UcAttendance> attendances)
        {
            Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US");

            Application excelApp = null;
            Workbook book = null;
            ChartObject grafico = null;

            Worksheet folha1 = null;
            Worksheet folha2 = null;
            Worksheet folha3 = null;

            try
            {
                excelApp = new Application
                   {
                       SheetsInNewWorkbook = 3
                   };
                
                book = excelApp.Workbooks.Add(Missing.Value);

                //Iniciar folhas de cálculo
                folha1 = (Worksheet)book.Worksheets[1];
                folha2 = (Worksheet)book.Worksheets[2];
                folha3 = (Worksheet)book.Worksheets[3];
                excelApp.ActiveWindow.DisplayGridlines = false;
                folha2.Activate();
                excelApp.ActiveWindow.DisplayGridlines = false;

                //format a folha 1
                FormatFolha1(folha1);

                //Preencher Verificador com as Colisões encontradas
                // Preencher Dados na worksheet 3 - Dados
                DataPreparation(book, collisions);

                //CRIAR UM GRAFICO NO EXCEL
                //Obtem todos os gráficos da folha
                grafico = PrepareFolha2(folha3, folha2);
                
                //guardar ficheiro
                folha1.Activate();
                book.Close(true, filename, Missing.Value);
                Console.WriteLine("Terminou");

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
            finally
            {
                #region Limpa Memória
                    if (grafico != null)
                    {
                        System.Runtime.InteropServices.Marshal.ReleaseComObject(grafico);
                        grafico = null;
                    }
                    if (folha1 != null)
                    {
                        System.Runtime.InteropServices.Marshal.ReleaseComObject(folha1);
                        folha1 = null;
                    }
                    if (folha2 != null)
                    {
                        System.Runtime.InteropServices.Marshal.ReleaseComObject(folha2);
                        folha2 = null;
                    }
                    if (folha3 != null)
                    {
                        System.Runtime.InteropServices.Marshal.ReleaseComObject(folha3);
                        folha3 = null;
                    }
                    if (book != null)
                    {
                        System.Runtime.InteropServices.Marshal.ReleaseComObject(book);
                        book = null;
                    }
                    if (excelApp != null)
                    {
                        excelApp.Quit();
                        System.Runtime.InteropServices.Marshal.ReleaseComObject(excelApp);
                        excelApp = null;
                    }
                    GC.Collect(); 
                #endregion
            } 
        }

        private static ChartObject PrepareFolha2(Worksheet folha3, _Worksheet folha2)
        {
            folha2.Name = "Grafico";

            ChartObjects chartObjects = (ChartObjects)folha2.ChartObjects(Missing.Value);

            //Adiciona um novo gráfico nas coordenadas indicadas
            var grafico = chartObjects.Add(10, 10, 900, 350);

            //obtem a informação para o gráfico
            Range range = folha3.get_Range("F:J", Missing.Value);
           

            grafico.Chart.ChartType = XlChartType.xl3DLine;
            grafico.Chart.SetSourceData(range, XlRowCol.xlColumns);

            grafico.Chart.HasTitle = true;
            grafico.Chart.ChartTitle.Text = "Verificador de Colisões";

            System.Runtime.InteropServices.Marshal.ReleaseComObject(range);
            range = null;
            System.Runtime.InteropServices.Marshal.ReleaseComObject(chartObjects);
            chartObjects = null;

            return grafico;
        }

        private static void DataPreparation(Workbook book, IDictionary<string, Collision> collisions)
        {
            var folha1 = (Worksheet)book.Worksheets[1];
            var folha3 = (Worksheet)book.Worksheets[3];

            //variaveis necessárias para preencher a folha1
            var colsUsed = 0;
            var previousWeekDay = 0;
            var startColumn = 2;
            var weekDays = new string[] { "2ª", "3ª", "4ª", "5ª", "6ª", "sáb" };

            //variaveis necessárias para preencher a folha3
            var linha = 2;
            folha3.Name = "Dados";
            folha3.Cells[1, 12] = "Detecção de Colisões";
            folha3.Cells[1, 1] = "UC1";
            folha3.Cells[1, 2] = "Tipo Aula";
            folha3.Cells[1, 3] = "UC2";
            folha3.Cells[1, 4] = "Tipo Aula";
            folha3.Cells[1, 5] = "Data Colisão";
            folha3.Cells[1, 6] = "Dia Semana";
            folha3.Cells[1, 7] = "Hora Inicio";
            folha3.Cells[1, 8] = "Hora Fim";
            folha3.Cells[1, 9] = "Num Colisões";
            folha3.Cells[1, 10] = "Duração";
            Range cel = folha3.get_Range("A1", "O1");
            cel.Font.Bold = true;
            cel.ColumnWidth = 12;

            foreach (string key in collisions.Keys)
            {
                Collision collision = collisions[key];
                
                #region Prepara dados na folha 1

                var weekDay = (int)collision.Data.DayOfWeek + 1;

                //se o dia da semana for diferente do anterior
                if (previousWeekDay != weekDay)
                {
                    if (previousWeekDay >= 2 && previousWeekDay <= 7)
                    {
                        //cabeçalho
                        var r = folha1.get_Range(folha1.Cells[2, startColumn], folha1.Cells[2, startColumn + colsUsed - 1]);
                        r.Merge(false);
                        r.Interior.ColorIndex = 40;
                        r.Value2 = weekDays[previousWeekDay - 2];
                        r.BorderAround(XlLineStyle.xlContinuous, XlBorderWeight.xlMedium, XlColorIndex.xlColorIndexAutomatic, 1);
                        System.Runtime.InteropServices.Marshal.ReleaseComObject(r);
                        r = null;

                        //celulas do dia
                        r = folha1.get_Range(folha1.Cells[3, startColumn], folha1.Cells[34, startColumn + colsUsed - 1]);
                        r.BorderAround(XlLineStyle.xlContinuous, XlBorderWeight.xlMedium, XlColorIndex.xlColorIndexAutomatic, 1);
                        System.Runtime.InteropServices.Marshal.ReleaseComObject(r);
                        r = null;
                    }

                    previousWeekDay = weekDay;
                    startColumn += colsUsed;
                    colsUsed = 0;
                }
                //calcula a linha inicial e final do range
                var l1 = 3 + collision.Start.Subtract(new TimeSpan(0, 8, 0, 0)).TotalMinutes / 30;
                var l2 = 2 + collision.End.Subtract(new TimeSpan(0, 8, 0, 0)).TotalMinutes / 30;

                //procura uma coluna vazia para o range horário, no máximo de 5
                for (var c = 0; c < 5; c++)
                {
                    var col = startColumn + c;
                    var validColumn = true;
                    Random rand = new Random();

                    //verifica se a coluna está vazia
                    for (var l = l1; l < l2; l++)
                    {
                        if (((Range)folha1.Cells[l, col]).Value2 != null)
                        {
                            validColumn = false;
                            break;
                        }
                    }
                    if (!validColumn) continue;

                    //se a coluna estiver livre
                    colsUsed = Math.Max(colsUsed, c + 1);
                    Range r = folha1.get_Range(folha1.Cells[l1, col], folha1.Cells[l2, col]);
                    r.Merge(false);
                    r.Interior.ColorIndex = 35 + rand.Next(1,10);
                    r.Value2 = string.Format("{0}\n{1}\n{2} est.", collision.Horario1.UcId, collision.Horario2.UcId, collision.Collisions);
                    System.Runtime.InteropServices.Marshal.ReleaseComObject(r);
                    r = null;
                    break;  //e pára a procura
                }

                #endregion

                #region Prepara dados na folha 3

                folha3.Cells[linha, 1] = collision.Horario1.UcId;
                folha3.Cells[linha, 2] = collision.Horario1.TipoAula;
                folha3.Cells[linha, 3] = collision.Horario2.UcId;
                folha3.Cells[linha, 4] = collision.Horario2.TipoAula;
                folha3.Cells[linha, 5] = collision.Data.Date;
                folha3.Cells[linha, 6] = (int)collision.Data.DayOfWeek + 1;
                folha3.Cells[linha, 7] = new DateTime(collision.Start.Ticks);
                folha3.Cells[linha, 8] = new DateTime(collision.End.Ticks);
                folha3.Cells[linha, 9] = collision.Collisions;
                folha3.Cells[linha, 10] = collision.Duration;
                linha++;

                #endregion
            }

            //faz a formatação do último dia
            if (previousWeekDay >= 2 && previousWeekDay <= 7)
            {
                //cabeçalho
                var r = folha1.get_Range(folha1.Cells[2, startColumn], folha1.Cells[2, startColumn + colsUsed]);
                r.Merge(false);
                r.Interior.ColorIndex = 40;
                r.Value2 = weekDays[previousWeekDay - 2];
                r.BorderAround(XlLineStyle.xlContinuous, XlBorderWeight.xlMedium, XlColorIndex.xlColorIndexAutomatic, 1);
                System.Runtime.InteropServices.Marshal.ReleaseComObject(r);
                r = null;

                //celulas do dia
                r = folha1.get_Range(folha1.Cells[3, startColumn], folha1.Cells[34, startColumn + colsUsed]);
                r.BorderAround(XlLineStyle.xlContinuous, XlBorderWeight.xlMedium, XlColorIndex.xlColorIndexAutomatic, 1);
                System.Runtime.InteropServices.Marshal.ReleaseComObject(r);
                r = null;
            }

            folha3.get_Range("G2", string.Format("H{0}", collisions.Keys.Count + 2)).NumberFormat = "hh:mm";

            //limpa qualquer referencia a objectos COM
            folha1 = null;
            folha3 = null;
        }

        private static void FormatFolha1(_Worksheet folha1)
        {
            
            folha1.Name = "Verificador";
            folha1.Cells[1, 4] = "Verificador de Horários - Trabalho Prático Integração de Sistemas 2009/2010";
            Range cel = folha1.get_Range("A1", "U2");
            cel.Font.Bold = true;
            cel.ColumnWidth = 8;
            
            Range celulas = folha1.get_Range("A3", "A34");
            celulas.Interior.ColorIndex = 46;
            celulas.RowHeight = 20.25;
            celulas.ColumnWidth = 6.29;

            var d = new DateTime().AddHours(8);

            foreach (Range cell in celulas)
            {
                
                cell.Cells[1, 1] = d;
                cell.NumberFormat = "hh:mm";
                cell.Font.Bold = true;
                    
                d = d.AddMinutes(30);
            }

            
            //limpa qualquer referencia a objectos COM
            System.Runtime.InteropServices.Marshal.ReleaseComObject(celulas);
            celulas = null;

        }

        #endregion
    }

}


